//
//  YYPhotoHandler.swift
//  CodeLibrary_Swift
//
//  Created by 陆文宇 on 2017/2/8.
//  Copyright © 2017年 陆文宇. All rights reserved.
//

import UIKit
import Photos

class YYPhotoHandler {
    
    /// 相册类型
    struct AssetCollectionType: OptionSet {
        static var `default`: AssetCollectionType = [regular, videos, recentlyAdded, userLibrary, selfPortraits, screenshots]
        static var regular: AssetCollectionType = AssetCollectionType(rawValue: 1 << 0)
        static var generic: AssetCollectionType = AssetCollectionType(rawValue: 1 << 1)
        static var panoramas: AssetCollectionType = AssetCollectionType(rawValue: 1 << 2)
        static var videos: AssetCollectionType = AssetCollectionType(rawValue: 1 << 3)
        static var favorites: AssetCollectionType = AssetCollectionType(rawValue: 1 << 4)
        static var timelapses: AssetCollectionType = AssetCollectionType(rawValue: 1 << 5)
        static var allHide: AssetCollectionType = AssetCollectionType(rawValue: 1 << 6)
        static var recentlyAdded: AssetCollectionType = AssetCollectionType(rawValue: 1 << 7)
        static var bursts: AssetCollectionType = AssetCollectionType(rawValue: 1 << 8)
        static var slomoVideos: AssetCollectionType = AssetCollectionType(rawValue: 1 << 9)
        static var userLibrary: AssetCollectionType = AssetCollectionType(rawValue: 1 << 10)
        static var selfPortraits: AssetCollectionType = AssetCollectionType(rawValue: 1 << 11)
        static var screenshots: AssetCollectionType = AssetCollectionType(rawValue: 1 << 12)
        static var all: AssetCollectionType = AssetCollectionType(rawValue: Int.max)
        static var none: AssetCollectionType = all
        
        typealias RawValue = Int
        var rawValue: RawValue
        init(rawValue: RawValue) {
            self.rawValue = rawValue
        }
        
        static func ==(l: AssetCollectionType, r: AssetCollectionType) -> Bool {
            if l.rawValue == Int.max || r.rawValue == Int.max {
                return true
            } else {
                return l.rawValue == r.rawValue
            }
        }
    }
    
    
    static var maxPickCount: Int = Int.max
    static var isRecord: Bool = false
    static var choosedPhotos: Array<String>?
    
    static let imageCache: NSCache<AnyObject, UIImage> = {
        let temp: NSCache<AnyObject, UIImage> = NSCache()
        temp.name = "PhotoHandlerCache"
        temp.evictsObjectsWithDiscardedContent = true
        return temp
    }()
    private init() {
        
    }
    
    /**
     *  判断相册读写权限
     *
     *  - Parameters:
     *      - authorized: 授权状态下执行
     *      - denied:     未授权状态下执行
     */
    static func available(authorized: @escaping () -> Void, denied: @escaping () -> Void) -> Void {
        if PHPhotoLibrary.authorizationStatus() == .authorized {
            authorized()
        } else if PHPhotoLibrary.authorizationStatus() == .notDetermined {
            PHPhotoLibrary.requestAuthorization({ (status) in
                if status == PHAuthorizationStatus.authorized {
                    authorized()
                } else if status == PHAuthorizationStatus.denied {
                    denied()
                }
            })
        } else {
            denied()
        }
    }
    
    /**
     *  照片预加载
     */
    static func preload(_ completionHandler: ((Array<YYPhotoModel>?) -> Void)? = nil) {
        available(authorized: {
            DispatchQueue.global().async {
                let album = (albums(with: .userLibrary)["albums"] as! Array<Dictionary<String, Any>>).first?["album"] as! PHAssetCollection
                let results = photos(in: album, isOriginal: true)
                DispatchQueue.main.async {
                    completionHandler?(results)
                }
            }
        }) {
            completionHandler?(nil)
            print("未打开照片读取权限")
        }
    }
    
    /**
     *  获取相册
     *  
     *  - Parameters:
     *      - assetCollectionTypes: 相册类型
     *      - defaultType:          默认类型
     *      - defaultTitle:         默认标题，当选择有regular类型相册时有效
     *
     *  - Returns: {"albums": albums, "defaultAlbumName": defaultAlbumName, "defaultAlbumIndex": defaultAlbumIndex}
     */
    static func albums(with assetCollectionTypes: YYPhotoHandler.AssetCollectionType, defaultType: YYPhotoHandler.AssetCollectionType = .default, defaultTitle: String? = nil) -> Dictionary<String, Any> {
        var assetCollectionTypeArray: Array<Dictionary<String, Int>> = []
        if assetCollectionTypes.contains(.regular) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.album.rawValue, "subtype": PHAssetCollectionSubtype.albumRegular.rawValue])
        }
        if assetCollectionTypes.contains(.generic) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumGeneric.rawValue])
        }
        if assetCollectionTypes.contains(.panoramas) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumPanoramas.rawValue])
        }
        if assetCollectionTypes.contains(.videos) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumVideos.rawValue])
        }
        if assetCollectionTypes.contains(.favorites) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumFavorites.rawValue])
        }
        if assetCollectionTypes.contains(.timelapses) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumTimelapses.rawValue])
        }
        if assetCollectionTypes.contains(.allHide) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumAllHidden.rawValue])
        }
        if assetCollectionTypes.contains(.recentlyAdded) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumRecentlyAdded.rawValue])
        }
        if assetCollectionTypes.contains(.bursts) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumBursts.rawValue])
        }
        if assetCollectionTypes.contains(.slomoVideos) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumSlomoVideos.rawValue])
        }
        if assetCollectionTypes.contains(.userLibrary) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumUserLibrary.rawValue])
        }
        if assetCollectionTypes.contains(.selfPortraits) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumSelfPortraits.rawValue])
        }
        if assetCollectionTypes.contains(.screenshots) {
            assetCollectionTypeArray.append(["type": PHAssetCollectionType.smartAlbum.rawValue, "subtype": PHAssetCollectionSubtype.smartAlbumScreenshots.rawValue])
        }
        
        var defaultType = defaultType
        var defaultSubtype: PHAssetCollectionSubtype = .any
        if defaultType == .default {
            if assetCollectionTypes.contains(.userLibrary) {
                defaultSubtype = .smartAlbumUserLibrary
            } else {
                defaultType = assetCollectionTypes
            }
        }
        switch defaultType {
        case YYPhotoHandler.AssetCollectionType.regular:
            defaultSubtype = .albumRegular
        case YYPhotoHandler.AssetCollectionType.generic:
            defaultSubtype = .smartAlbumGeneric
        case YYPhotoHandler.AssetCollectionType.panoramas:
            defaultSubtype = .smartAlbumPanoramas
        case YYPhotoHandler.AssetCollectionType.videos:
            defaultSubtype = .smartAlbumVideos
        case YYPhotoHandler.AssetCollectionType.favorites:
            defaultSubtype = .smartAlbumFavorites
        case YYPhotoHandler.AssetCollectionType.timelapses:
            defaultSubtype = .smartAlbumTimelapses
        case YYPhotoHandler.AssetCollectionType.allHide:
            defaultSubtype = .smartAlbumAllHidden
        case YYPhotoHandler.AssetCollectionType.recentlyAdded:
            defaultSubtype = .smartAlbumRecentlyAdded
        case YYPhotoHandler.AssetCollectionType.bursts:
            defaultSubtype = .smartAlbumBursts
        case YYPhotoHandler.AssetCollectionType.slomoVideos:
            defaultSubtype = .smartAlbumSlomoVideos
        case YYPhotoHandler.AssetCollectionType.userLibrary:
            defaultSubtype = .smartAlbumUserLibrary
        case YYPhotoHandler.AssetCollectionType.selfPortraits:
            defaultSubtype = .smartAlbumSelfPortraits
        case YYPhotoHandler.AssetCollectionType.screenshots:
            defaultSubtype = .smartAlbumScreenshots
        default:
            break
        }
        
        assert(assetCollectionTypeArray.contains(where: { (type) -> Bool in
            if type["subtype"] == defaultSubtype.rawValue {
                return true
            } else {
                return false
            }
        }), "assetCollectionTypes:\(assetCollectionTypes) do not contain defaultTypes:\(defaultType)")
        
        
        var results: Dictionary<String, Any> = [:]
        var albums: Array<Dictionary<String, Any>> = []
        for assetCollectionType in assetCollectionTypeArray {
            let systemAlbums: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType(rawValue: assetCollectionType["type"]!)!, subtype: PHAssetCollectionSubtype(rawValue: assetCollectionType["subtype"]!)!, options: nil)
            for i in 0 ..< systemAlbums.count {
                let album: PHAssetCollection = systemAlbums[i]
                let photos: Array<YYPhotoModel> = YYPhotoHandler.photos(in: album, isOriginal: true)
                if photos.count == 0 {
                    if defaultSubtype != album.assetCollectionSubtype {
                        continue
                    } else if album.assetCollectionSubtype == .albumRegular && defaultTitle != album.localizedTitle {
                        continue
                    }
                }
                
                let albumTitle: String = album.localizedTitle!
                let photoNumber: Int = photos.count
                let lastPhoto: UIImage? = photos.last?.image
                var dic: Dictionary<String, Any> = ["title": albumTitle, "number": photoNumber, "album": album]
                dic["image"] = lastPhoto
                albums.append(dic)
                
                if album.assetCollectionSubtype == defaultSubtype {
                    if defaultSubtype == .albumRegular {
                        assert(defaultTitle != nil && !defaultTitle!.isEmpty, "当选择自定义相簿的时候 defaultTitle must not be nil or empty string")
                        if albumTitle == defaultTitle! {
                            results["defaultAlbumIndex"] = albums.endIndex - 1
                            results["defaultAlbumName"] = albumTitle
                        }
                    } else {
                        results["defaultAlbumIndex"] = albums.endIndex - 1
                        results["defaultAlbumName"] = albumTitle
                    }
                }
            }
        }
        
        results["albums"] = albums
        assert(results["defaultAlbumName"] != nil, "no exist defaultTitle:\(defaultTitle ?? "nil")")
        return results
    }
    
    /**
     *  获取照片
     *
     *  - Parameters:
     *      - assetCollection: 相册
     *      - isOriginal:      是否是原图
     *      - widthScale:      宽度缩小比例，默认4
     *      - heightScale:     高度缩小比例，默认7
     *
     * - Returns: 照片数组
     */
    static func photos(in assetCollection: PHAssetCollection, isOriginal: Bool, widthScale: CGFloat = 4, heightScale: CGFloat = 7) -> Array<YYPhotoModel> {
        var photos: Array<YYPhotoModel> = []
        let options: PHImageRequestOptions = PHImageRequestOptions()
        options.isSynchronous = true
        
        let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollection, options: nil)
        for i in 0 ..< assets.count {
            let asset: PHAsset = assets[i]
            if imageCache.object(forKey: asset.localIdentifier as AnyObject) != nil {
                let photo: YYPhotoModel = YYPhotoModel(image: imageCache.object(forKey: asset.localIdentifier as AnyObject)!, asset: asset)
                photos.append(photo)
                continue
            }
            
            let size: CGSize = isOriginal ? CGSize(width: UIScreen.main.bounds.size.width * 2 / widthScale, height: UIScreen.main.bounds.size.height * 2 / heightScale) : .zero
            PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .default, options: options) { (result, info) in
                if let image = result {
                    self.imageCache.setObject(image, forKey: asset.localIdentifier as AnyObject)
                    
                    let photo: YYPhotoModel = YYPhotoModel(image: image, asset: asset)
                    photos.append(photo)
                }
            }
        }
        return photos
    }
    
    static func createAlbum(with identifier: String) -> Bool {
        if !hasAlbum(with: identifier) {
            PHPhotoLibrary.shared().performChanges({
                PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: identifier)
            }, completionHandler: { (success, error) in
                if error == nil {
                    
                } else {
                    print(error!.localizedDescription)
                }
            })
            return true
        } else {
            return false
        }
    }
    
    /**
     *  判断是否存在某regular相册
     *
     *  - Parameters:
     *      - identifier: 相册标题
     *
     * - Returns: 判断结果
     */
    static func hasAlbum(with identifier: String) -> Bool {
        if album(with: identifier) != nil {
            return true
        }
        return false
    }
    
    /**
     *  获取某regular相册
     *
     *  - Parameters:
     *      - identifier: 相册标题
     *
     * - Returns:  如果存在，返回该相册，否则返回nil
     */
    static func album(with identifier: String) -> PHAssetCollection? {
        let assetCollections: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
        for i in 0 ..< assetCollections.count {
            let album: PHAssetCollection = assetCollections[i]
            if album.localizedTitle == identifier {
                return album
            }
        }
        return nil
    }
    
    /**
     *  将图片添加到相机胶卷
     *
     *  - Parameters:
     *      - images:            要添加的图片数组
     *      - completionHandler: 完成后执行
     */
    static func addImagesIntoUserLibrary(images: Array<UIImage>, completionHandler: @escaping (PHAsset?, Bool, Error?) -> Void) -> Void {
        PHPhotoLibrary.shared().performChanges({
            for image in images {
                PHAssetCreationRequest.creationRequestForAsset(from: image)
            }
        }, completionHandler: { (success, error) in
            let assetCollections: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)
            let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollections.lastObject!, options: nil)
            completionHandler(success ? assets.lastObject! : nil, success, error)
        })
    }
    
    /**
     *  将相机胶卷里的最后一张照片添加到指定相册
     *
     *  - Parameters:
     *      - albumName:         相册标题
     *      - completionHandler: 完成后执行
     */
    static func addLastPhotoInUserLibraryToAlbum(albumName: String, completionHandler: @escaping (PHAsset?, Bool, Error?) -> Void) -> Void {
        let assetCollections: PHFetchResult<PHAssetCollection> = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumUserLibrary, options: nil)
        let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollections.lastObject!, options: nil)
        addPhotos([assets.lastObject!], inAssetCollectionWith: albumName) { (success, error) in
            completionHandler(assets.lastObject, success, error)
        }
    }
    
    /**
     *  添加照片
     *
     *  - Parameters:
     *      - assets:            照片资源数组
     *      - albumName:         相册标题
     *      - completionHandler: 完成后执行
     */
    static func addPhotos(_ assets: Array<PHAsset>, inAssetCollectionWith albumName: String, completionHandler: @escaping (Bool, Error?) -> Void) -> Void {
        PHPhotoLibrary.shared().performChanges({ 
            if let assetCollection = self.album(with: albumName) {
                assetCollection.canPerform(.addContent)
                PHAssetCollectionChangeRequest(for: assetCollection)?.addAssets(NSArray(array: assets))
            } else {
                completionHandler(false, NSError(domain: "YYPhotoHandler", code: 0, userInfo: ["errmsg": "相册名字不存在"]))
            }
        }, completionHandler: { (success, error) in
            completionHandler(success, error)
        })
    }
    
    /**
     *  移除照片
     *
     *  - Parameters:
     *      - assets:            照片资源数组
     *      - albumName:         相册标题
     *      - completionHandler: 完成后执行
     */
    static func removePhotos(_ assets: Array<PHAsset>, inAssetCollectionWith albumName: String, completionHandler: @escaping (Bool, Error?) -> Void) -> Void {
        PHPhotoLibrary.shared().performChanges({
            if let assetCollection = self.album(with: albumName) {
                assetCollection.canPerform(.removeContent)
                PHAssetCollectionChangeRequest(for: assetCollection)?.removeAssets(NSArray(array: assets))
            } else {
                completionHandler(false, NSError(domain: "YYPhotoHandler", code: 0, userInfo: ["errmsg": "相册名字不存在"]))
            }
        }, completionHandler: { (success, error) in
            completionHandler(success, error)
        })
    }
    
}
